// 摘自《Linux环境编程 从应用到内核》
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <fcntl.h>

#define BUF_4K          4 * 1024
#define BUF_8K          8 * 1024
#define BUF_12K         12 * 1024

// 第一个子进程每次向管道写入4096字节的A字符，循环10次；第二个子进程向管道写入8192字节（4096*2）
// 的B字符，循环10次；第三个子进程每次向管道写入12288字节（4096*3）的C字符，循环10次。父进程负责从管道里面读取内容，写入到test.txt
// 文件。
// 由于三个子进程和一个父进程是同时运行的，考虑到进程调度的因素，每次执行写入管道和从管道读取的时序并不完全一样。因此每次执
// 行，产生的test.txt文件也不相同。对于每次写入8KB和每次写入12KB的情况，尽管管道不保证原子性，但是其内容也不是每次都必然会混入其他
// 进程的写入。
// 多次执行该程序，总会遇到某次8KB或12KB的写入，中间混杂了其他字符。

int main(void)
{
    char a[BUF_4K];
    char b[BUF_8K];
    char c[BUF_12K];

    memset(a, 'A', sizeof(a));
    memset(b, 'B', sizeof(b));
    memset(c, 'C', sizeof(c));

    int pipefd[2];
    int ret = pipe(pipefd);

    if (ret == -1) {
        fprintf(stderr, "failed to create pipe (%s)\n", strerror(errno));
        return 1;
    }

    pid_t pid;

    pid = fork();
    if (pid == 0) { // 第一个子进程
        close(pipefd[0]);
        int loop = 0;
        while (loop++ < 10) {
            ret = write(pipefd[1], a, sizeof(a));
            printf("apid=%d write %d bytes to pipe\n", getpid(), ret);
        }
        exit(0);
    }

    pid = fork();
    if (pid == 0) { // 第二个子进程
        close(pipefd[0]);
        int loop = 0;
        while (loop++ < 10) {
            ret = write(pipefd[1], b, sizeof(b));
            printf("bpid=%d write %d bytes to pipe\n", getpid(), ret);
        }
        exit(0);
    }

    pid = fork();
    if (pid == 0) { // 第三个子进程
        close(pipefd[0]);
        int loop = 0;
        while (loop++ < 10) {
            ret = write(pipefd[1], c, sizeof(c));
            printf("cpid=%d write %d bytes to pipe\n", getpid(), ret);
        }
        exit(0);
    }

    close(pipefd[1]);
    sleep(1);

    int fd = open("test.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
    char buf[1024 * 4] = {0};
    int n = 1;

    while (1) {
        ret = read(pipefd[0], buf, sizeof(buf));
        if (ret == 0) {
            break;
        }

        printf("n=%02d pid=%d read %d bytes from pipe buf[4095]=%c\n", n++, getpid(), ret, buf[4095]);
        write(fd, buf, ret);
    }

    return 0;
}
